import type { TreeLayout, TreeLike } from "@/models";
import { Position, type Edge, type Elements, type Node, type XYPosition } from "@vue-flow/core";
import { cloneDeep } from "lodash-es";

export const DEFAULT_LAYOUT: TreeLayout = { mode: "vertical", gap: { x: 10, y: 50 }, size: { w: 150, h: 36 } };

export function useTreeGui(flat: TreeLike[]) {
  const _flat = cloneDeep(flat);

  function toTrees() {
    const trees: TreeLike[] = [];
    _flat.forEach((tl) => {
      const parent = _flat.find((p) => p.id === tl.pid);
      if (parent) {
        if (!parent.children) parent.children = [];
        parent.children.push(tl);
        tl.parent = parent;
      } else {
        trees.push(tl);
      }
    });

    return trees;
  }

  function toPool(tree: TreeLike) {
    const pool: TreeLike[][] = [];

    const send2pool = (tl: TreeLike, depth: number) => {
      tl.depth = depth;
      if (pool[depth] === undefined) pool[depth] = [];
      pool[depth].push(tl);

      if (tl.children !== undefined && tl.children.length > 0) {
        // 真子集
        for (const child of tl.children) {
          send2pool(child, depth + 1);
        }
      } else {
        // 虚子集
        if (hasNephew(tl)) {
          const vchild: TreeLike = { id: "", name: "", pid: tl.id, parent: tl, virtual: true };
          tl.children = [vchild];
          send2pool(vchild, depth + 1);
        }
      }
    };

    const hasNephew = (tl: TreeLike): boolean => {
      const brothers = findBrothers(tl);
      let found = false;

      for (const brother of brothers) {
        if (brother.children !== undefined && brother.children.findIndex((c) => !c.virtual) > -1) {
          found = true;
          break;
        }
      }

      return found;
    };

    const findBrothers = (tl: TreeLike) => {
      const brothers: TreeLike[] = [];

      if (tl.parent !== undefined && tl.parent.children !== undefined && tl.parent.children.length > 0) {
        brothers.push(...tl.parent.children.filter((c) => c.id !== tl.id));

        const uncles = findBrothers(tl.parent);
        for (const uncle of uncles) {
          if (uncle.children !== undefined && uncle.children.length > 0) brothers.push(...uncle.children);
        }
      }

      return brothers;
    };

    send2pool(tree, 0);

    // 获取所有深度层级中的最大节点数
    const max = Math.max(...pool.map((p) => p.length));

    // 设置每个节点的一般化序列 0~1
    for (let i = pool.length - 1; i >= 0; i--) {
      const row = pool[i];
      const lns: number[] = [];

      for (let j = 0; j < row.length; j++) {
        const cell = row[j];
        if (!cell.children) {
          cell.length = 1 / max;
        } else {
          cell.length = cell.children.map((c) => c.length || 1).reduce((a, b) => a + b, 0);
        }
        lns.push(cell.length);

        const prefix = j > 0 ? lns.slice(0, j).reduce((a, b) => a + b, 0) : 0;
        cell.sequence = prefix + cell.length / 2;
      }
    }

    return { pool, max };
  }

  function toPools() {
    const trees = toTrees();
    const pools: { pool: TreeLike[][]; max: number }[] = [];
    trees.forEach((t) => pools.push(toPool(t)));
    return pools;
  }

  function toElements(layout?: TreeLayout) {
    const normalize = {
      layout(layout: TreeLayout) {
        const gap = { justify: 0, align: 0 };
        const size = { justify: 0, align: 0 };
        const position = { source: Position.Bottom, target: Position.Top };

        if (layout.mode === "vertical") {
          gap.align = layout.gap.x;
          gap.justify = layout.gap.y;
          size.align = layout.size.w;
          size.justify = layout.size.h;
          position.source = Position.Bottom;
          position.target = Position.Top;
        } else {
          gap.align = layout.gap.y;
          gap.justify = layout.gap.x;
          size.align = layout.size.h;
          size.justify = layout.size.w;
          position.source = Position.Right;
          position.target = Position.Left;
        }

        return { gap, size, position };
      },
    };

    const unnormalize = {
      position(layout: TreeLayout, tree: TreeLike, prefix: number, smax: number, dmax: number) {
        const position: XYPosition = { x: 0, y: 0 };
        const positionAlign = prefix + smax * (tree.sequence || 0);
        const positionJustify = dmax * (tree.depth || 0);

        if (layout.mode === "vertical") {
          position.x = positionAlign;
          position.y = positionJustify;
        } else {
          position.x = positionJustify;
          position.y = positionAlign;
        }

        return position;
      },
    };

    const _layout: TreeLayout = layout || DEFAULT_LAYOUT;
    const elements: Elements = [];
    const pools = toPools();
    const { gap, size, position } = normalize.layout(_layout);

    for (let k = 0; k < pools.length; k++) {
      const { pool, max } = pools[k];
      const offset = pools
        .slice(0, k)
        .map((p) => p.max)
        .reduce((a, b) => a + b, 0);
      const prefix = size.align * offset + gap.align * offset;
      const smax = size.align * max + gap.align * (max - 1);
      const dmax = size.justify + gap.justify;

      console.log("offset", offset, prefix, smax);

      for (let i = 0; i < pool.length; i++) {
        const row = pool[i];
        for (let j = 0; j < row.length; j++) {
          const cell = row[j];
          if (cell.virtual) continue;

          const node: Node<TreeLike> = {
            id: cell.id.toString(),
            label: cell.name,
            sourcePosition: position.source,
            targetPosition: position.target,
            position: unnormalize.position(_layout, cell, prefix, smax, dmax),
            width: _layout.size.w,
            height: _layout.size.h,
          };

          if (cell.parent === undefined) node.type = "input";
          if (cell.children === undefined || cell.children.every((c) => c.virtual)) node.type = "output";

          elements.push(node);

          if (cell.pid !== undefined) {
            const edge: Edge<TreeLike> = {
              id: `edge-${cell.pid}-${cell.id}`,
              source: cell.pid.toString(),
              target: cell.id.toString(),
              type: "smoothstep",
              style: { stroke: "orangered" },
              animated: true,
            };
            elements.push(edge);
          }
        }
      }
    }

    return elements;
  }

  return { toTrees, toPools, toElements };
}
